#!/bin/env python3.6
# -*- coding: utf-8 -*-

import re
import time
import random
import json
import sys
import re
import mysql.connector
import redis
import requests

from lib.logger import logger
from lib.conf import get_key


def get_listing_date(code=None):
    """
    获取股票上市日期
    :param code: str 股票代码
    :return:
    """
    logger.debug("function start...")
    if not re.match("[0369][0-9]{5}", str(code)):
        logger.error("code should be a valid stock code without sz or sh but get:%s" % code)
        return None

    r = redis.StrictRedis(host="106.14.166.29" if sys.platform == 'darwin' else get_key("redis", "host"),
                          port=get_key("redis", "port"),
                          db=get_key("redis", "db"),
                          password=get_key("redis", "password"))

    result = r.get('stock_listing_date_%s' % code)
    if result is not None:
        logger.debug("return from redis")
        data = result.decode("utf-8")
        return eval(data)
    config = {
        'host': "106.14.166.29" if sys.platform == 'darwin' else get_key("mysql", "host"),
        'user': get_key("mysql", "user"),
        'password': get_key("mysql", "password"),
        'port': get_key("mysql", "port"),
        'database': get_key("mysql", "db"),
        'raise_on_warnings': True,
    }
    cnx = mysql.connector.connect(**config)
    cursor = cnx.cursor()
    sql_query = "SELECT code,name,dateToMarket, status FROM stock_info where code={code}".format(code=code)
    cursor.execute(sql_query)
    data = cursor.fetchone()

    if data is None:
        result = None
    else:
        result = {
            "code": data[0],
            "name": data[1],
            "listing_date": data[2],
            "status": data[3]
        }
        r.set(name="stock_listing_date_%s" % result['code'], value=result, ex=86400, nx=True)

    cursor.close()
    cnx.close()

    logger.debug("function end.")
    return result


def get_k_data(code=None, start=None, end=None, ktype='D', retry_count=3, pause=0.1):
    """

    :param code: str 股票代码 格式: 600121
    :param start: str 起始日期 格式: 2018-01-05
    :param end: str 结束日期 默认最新 格式 2018-01-05
    :param ktype: char 数据类型 D=日k线 W=周 M=月 5=5分钟 15=15分钟 30=30分钟 60=60分钟，默认为D
    :param retry_count: int 失败重试次数, 因要多次请求http接口
    :param pause: float 多次请求http服务时间间隔
    :return: dict
    """

    """
    start=None,end=None
    start=None,end=xxx
    start=xxx,end=None
    start=xxx,end=xxx(year=year)
    start=xxx,end=xxx(year<>year)
    """
    start_time = time.time()

    result = []

    # 参数校验
    if ktype == "D":
        k_type = "day"
    elif ktype == "W":
        k_type = "week"
        # http://ifzq.gtimg.cn/appstock/app/kline/mkline?param=sh600321,m60,,640&_var=m60_today&r=0.6461524627215663
    if not re.match("[02369][0-9]{5}", str(code)):
        logger.error("code should be a valid stock code without sz or sh but get:%s" % code)
        return None
    if pause and not isinstance(pause, float):
        logger.warning("pause should be a float number but get {t}:{pause}, using default value: 0.01"
                       .format(t=type(pause), pause=pause))
    if retry_count and not isinstance(retry_count, int):
        logger.warning("retry_count should be an int number but get {t}:{retry_count}, using default value: 3"
                       .format(t=type(retry_count), retry_count=retry_count))
    try:
        if start is not None and start is not "":
            time.strptime(start, "%Y-%m-%d")
        else:
            start = get_listing_date(code=code)['listing_date'][:10]  # 获取股票上市日期，从mysql里面读取
        if end is not None and end is not "":
            time.strptime(end, "%Y-%m-%d")
        else:
            end = time.strftime("%Y-%m-%d", time.localtime(time.time() + 24 * 3600))
    except ValueError as e:
        logger.error("format error: {start} or {end} should be format like 2018-01-05".format(start=start, end=end))
        return None

    symbol = 'sh%s' % code if code[:1] in ['5', '6', '9'] else 'sz%s' % code

    urls = []
    for year in range(int(start[:4]), int(end[:4]) + 1):
        if year == int(start[:4]):
            _start = start
        else:
            _start = "{year}-01-01".format(year=year)
        if year == int(end[:4]):
            _end = end
        else:
            _end = "{year}-12-31".format(year=year)
        url = "http://web.ifzq.gtimg.cn/appstock/app/fqkline/get?" \
              "_var=kline_dayqfq{year}&param={symbol},{k_type},{start},{end},320,qfq&r={r}" \
            .format(year=year, symbol=symbol, k_type=k_type, start=_start, end=_end, r=random.random())
        urls.append(url)
    for url in urls:
        logger.debug(url)
        response = requests.get(url=url, timeout=10)
        data = response.text["kline_dayqfq2018=".__len__():]
        data = json.loads(data)["data"]
        if "qfqday" in data[symbol].keys() and data[symbol]['qfqday'] is not None:
            result.extend([{
                "code": str(code),
                "date": str(item[0]),
                "open": float(item[1]),
                "close": float(item[2]),
                "high": float(item[3]),
                "low": float(item[4]),
                "volume": int(float(item[5]))
            } for item in data[symbol]['qfqday']])
        elif "day" in data[symbol].keys() and data[symbol]['day'] is not None:
            result.extend([{
                "code": str(code),
                "date": str(item[0]),
                "open": float(item[1]),
                "close": float(item[2]),
                "high": float(item[3]),
                "low": float(item[4]),
                "volume": int(float(item[5]))
            } for item in data[symbol]['day']])

        time.sleep(random.random())
    logger.debug("function using time {} s get_k_data".format(time.time()-start_time))
    return result


def get_k_data_db(code=None, start=None, end=None):
    """
    从数据库中读取数据, 避免一直读取同一个网站而被封IP
    :param code: str 股票代码 格式: 600121
    :param start: str 起始日期 格式: 2018-01-05
    :param end: str 结束日期 默认最新 格式 2018-01-05
    :return: dict
    """
    logger.debug("FUN start at {}".format(time.time()))
    result = []

    if not re.match("[0369][0-9]{5}", str(code)):
        logger.error("code should be a valid stock code without sz or sh but get:%s" % code)
        return None
    try:
        if start is not None and start is not "":
            time.strptime(start, "%Y-%m-%d")
        else:
            start = get_listing_date(code=code)['listing_date'][:10]  # 获取股票上市日期，从mysql里面读取
        if end is not None and end is not "":
            time.strptime(end, "%Y-%m-%d")
        else:
            end = time.strftime("%Y-%m-%d", time.localtime(time.time() + 24 * 3600))
    except ValueError:
        logger.error("format error: {start} or {end} should be format like 2018-01-05".format(start=start, end=end))
        return None

    key_in_redis = "{code}_{start}_{end}".format(code=code, start=start.replace("-", ""), end=end.replace("-", ""))
    r = redis.StrictRedis(host="106.14.166.29" if sys.platform == 'darwin' else get_key("redis", "host"),
                          port=get_key("redis", "port"),
                          db=1,
                          password=get_key("redis", "password"))
    data = r.get(key_in_redis)
    if data is not None:
        logger.debug("function get_k_data_db return from redis")
        return eval(data)
    config = {
        'host': "106.14.166.29" if sys.platform == 'darwin' else get_key("mysql", "host"),
        'user': get_key("mysql", "user"),
        'password': get_key("mysql", "password"),
        'port': get_key("mysql", "port"),
        'database': get_key("mysql", "db"),
        'raise_on_warnings': True,
    }
    table_name = "stock_{code}".format(code=code)
    cnx = mysql.connector.connect(**config)
    cursor = cnx.cursor()
    sql_query = "SELECT code,date,open,close,high,low FROM " \
                "`{table}` WHERE `date` >= '{start}' and `date` <= '{end}';".format(
                    table=table_name, start=start, end=end)
    cursor.execute(sql_query)
    data = cursor.fetchall()
    if data is None:
        cursor.close()
        cnx.close()
        result = None
        return result
    for item in data:
        result.append({
            "code": item[0],
            "date": str(item[1]),
            "open": item[2],
            "close": item[3],
            "high": item[4],
            "low": item[5]
        })
    r.set(name=key_in_redis, value=result, ex=24*3600)
    cursor.close()
    cnx.close()
    logger.debug("FUN start at {}".format(time.time()))
    return result


def get_rates(code=None):
    """
    :param code: str stock code
    :return: list rate of increase
    """
    result = []
    data = get_k_data(code=code)
    result.append((data[0]["close"]-data[0]["open"])/data[0]["open"])
    for i in range(1, data.__len__()):
        result.append(data[i]["close"]/data[i-1]["close"]-1)
    return result


def get_listing_stocks():
    """
    获取当前上市股票列表
    :return:
    """
    config = {
        'host': "106.14.166.29" if sys.platform == 'darwin' else get_key("mysql", "host"),
        'user': get_key("mysql", "user"),
        'password': get_key("mysql", "password"),
        'port': get_key("mysql", "port"),
        'database': get_key("mysql", "db"),
        'raise_on_warnings': True,
    }
    cnx = mysql.connector.connect(**config)
    cursor = cnx.cursor()
    fields = [
        "code", "name", "dateToMarket", "status", "exchange"
    ]
    fields_str = ",".join(fields)
    sql = "select {} from stock_info WHERE `status`='L' ORDER BY dateTOMarket DESC;".format(fields_str)
    cursor.execute(sql)
    query_result = cursor.fetchall()

    cursor.close()
    cnx.close()

    field_num = fields.__len__()
    result = []
    for row in query_result:
        item = {}
        for i in range(field_num):
            item[fields[i]] = row[i]
        result.append(item)
    return result


def get_trade_detail(date=None, code=None):
    """
    获取股票指定日期每笔交易详情
    :param date: str %Y-%m-%d
    :param code: str [0-9]{6}
    :return: dict
    """
    result = {
        "date": date,
        "symbol": code,
        "data": []
    }
    params = {
        "date": date,
        "symbol": code
    }

    URL = "http://market.finance.sina.com.cn/downxls.php"

    # 参数校验
    try:
        time.strptime(str(date), "%Y-%m-%d")
    except ValueError as e:
        logger.error(e)
        return None
    if not re.match("[0-9]{6}", str(code)):
        logger.error("code should be a valid stock code but get {}".format(code))
        return None

    if code.startswith("600"):
        params["symbol"] = "sh{}".format(code)
    else:
        params["symbol"] = "sz{}".format(code)

    ret = requests.get(url=URL, params=params)
    if re.search(pattern="window\.close\(\)", string=ret.text):
        logger.error("current date {date} has no data".format(date=date))
        return None
    data = ret.text.split("\n")[1:-1]
    data.reverse()

    for row in data:
        row = row.split()
        if row[5] == 'бРел':
            types = 'buy'
        elif row[5] == 'бТел':
            types = 'sale'
        elif row[5] == 'жпптел':
            types = 'middle'
        else:
            types = 'unknown'
        item = {
            "time": row[0],
            'price': row[1],
            # 'rate': row[2],
            'flow': row[2],
            'volume': row[3],
            'e': row[4],
            'type': types,
        }
        result["data"].append(item)
    return result


def get_today_market_status():
    """
    获取当日上证或深证股票状态：交易中，休市
    注意返回的是当前时间点的交易状态
    :return: dict
    """
    curdate = time.strftime("%Y-%m-%d", time.localtime())
    code = "sh000002"
    URL = "http://web.ifzq.gtimg.cn/appstock/app/fqkline/get?" \
          "_var=kline_dayhfq&param={code},day,{curdate},,32000,hfq".format(curdate=curdate, code=code)
    response = requests.get(url=URL)
    if response.status_code != 200:
        print("can not get http response: %s, %s" % (response.status_code, response.text))
        return None

    content = json.loads(response.text.replace("kline_dayhfq=", ""))
    text = content["data"][code]["qt"]["market"][0]
    text = text.split('|')
    curtime = text.pop(0)
    result = {
        "time": curtime,
    }
    for market in text:
        item = market.split('_')
        result[item[0]] = {
            "status": item[1],
            "msg": item[2]
        }
    return result


def is_open(date=time.strftime("%Y-%m-%d", time.localtime())):
    """
    判断给定日期是否为交易日
    判断方法为: 拉去某只股票在该日的 数据  有就是交易日 没有就是非交易日
    该股票应该为不存在停牌退市等情况的股票 所以是上证综合指数
    :param date: str 日期
    :return: boolean True 交易日 False 非交易日
    """
    URL = "http://api.finance.ifeng.com/akdaily/?code=sh000001&type=last"
    ret = requests.get(url=URL)
    if ret.status_code != 200:
        logger.error("http code get {}, message:".format(ret.status_code, ret.text))
        return None
    data = ret.json()["record"]
    open_dates = [item[0] for item in data]
    return False if date not in open_dates else True
